package com.cms.bind_cpmpiler

import com.cms.bind_annotation.BindView
import com.cms.bind_cpmpiler.ProcessorConfig.PARAMETER_CLASS_NAME
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asTypeName
import java.io.File
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Messager
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedAnnotationTypes
import javax.annotation.processing.SupportedSourceVersion
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
import javax.lang.model.type.TypeKind
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
import javax.tools.Diagnostic

@SupportedAnnotationTypes("com.cms.bind_annotation.BindView")
@AutoService(Processor::class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
class BindViewProcessor : AbstractProcessor() {

    private var elementTools: Elements? = null
    private var typeTool: Types? = null
    private var messager: Messager? = null

    private var activityElement: TypeElement? = null

    private var viewGetElement: TypeElement? = null

    companion object {
        const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"

    }
    private val map:HashMap<Element,MutableList<Element>> = hashMapOf()




    override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        elementTools = processingEnv.elementUtils
        typeTool = processingEnv.typeUtils
        messager = processingEnv.messager

        activityElement = elementTools?.getTypeElement(ProcessorConfig.TYPE_ACTIVITY)
        viewGetElement = elementTools?.getTypeElement(ProcessorConfig.PARAMETER_CLASS_NAME)
    }

    override fun process(
        annotations: MutableSet<out TypeElement>?,
        roundEnv: RoundEnvironment?
    ): Boolean {


        val elements = roundEnv?.getElementsAnnotatedWith(BindView::class.java)
        if (elements.isNullOrEmpty()) return false
        val iterator = elements.iterator()
        while (iterator.hasNext()) {
            val next = iterator.next()
            if (next.kind == ElementKind.FIELD) {
                if (map.containsKey(next.enclosingElement)){
                    map[next.enclosingElement]?.add(next)
                }else{
                    val list = mutableListOf<Element>()
                    list.add(next)
                    map[next.enclosingElement] = list
                }
            } else {
                messager?.printMessage(Diagnostic.Kind.ERROR, "BindView 注解只能使用在成员属性上")
                return false
            }

        }
        if (map.isEmpty()) return true
        map.forEach { t, u ->
            messager?.printMessage(
                Diagnostic.Kind.NOTE,
                "type-----:" +t.simpleName
            )

            val fileSpec = FileSpec.builder("com.cms.processor", "${t.simpleName}Parameter")
            val methodBuilder = FunSpec.builder("getView")
                .addParameter("target",Any::class)
                .addModifiers(KModifier.OVERRIDE)
            u.forEach {
                processBindView(it,methodBuilder)
            }
            val classBuilder = TypeSpec.classBuilder("${t.simpleName}$PARAMETER_CLASS_NAME")
                .addSuperinterface(ClassName.bestGuess(ProcessorConfig.TYPE_PARAMETERGET))
                .addFunction(methodBuilder.build())
                .build()

            val outDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
            fileSpec.addType(classBuilder).build().writeTo(File(outDir))

        }


        return false
    }

    private fun processBindView(element: Element, methodBuilder: FunSpec.Builder) {
        val bindView = element.getAnnotation(BindView::class.java)
        messager?.printMessage(
            Diagnostic.Kind.NOTE,
            "type-----:" +element.enclosingElement.simpleName
        )


        val methodContent = "activity."+element.simpleName.toString()+" = activity.findViewById("+bindView.value+")"
        methodBuilder.addStatement("val activity = target as %T",ClassName.bestGuess(element.enclosingElement.toString())  )
        methodBuilder.addStatement(methodContent)
    }
}